package com.ansion.baseui.base;

import android.app.Activity;

import androidx.annotation.CallSuper;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;

import com.ansion.basetool.observer.ObjectManager;
import com.ansion.basetool.preconditions.Preconditions;
import com.ansion.log.VLog;
import com.ansion.utils.ThreadUtil;
import com.ansion.utils.lifecycle.LifeCycleHelper;
import com.ansion.utils.thread.SafeRunnable;
import com.ansion.utils.thread.ThreadPool;

import java.lang.ref.Reference;
import java.lang.ref.WeakReference;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;


/**
 * @author zhaoyuntao
 * date 2021/6/28
 */
@SuppressWarnings("rawtypes")
public class TBasePresenter<A> {
    private final StringBuilder detachedState = new StringBuilder();
    private Reference<A> reference;
    private BroadcastSupport broadcastSupport;
    private final ObjectManager<TPresenterProxy<? extends TBasePresenter, ?>> manager = new ObjectManager<>(false);

    public TBasePresenter() {
    }

    final public void addProxy(TPresenterProxy<? extends TBasePresenter, ?> proxy) {
        manager.addObject(proxy);
    }

    final public <Proxy extends TPresenterProxy<? extends TBasePresenter, ?>> Proxy getProxy(Class<Proxy> clazz) {
        Proxy t = manager.pickObject(clazz);
        if (t == null) {
            throw new RuntimeException("Have you registered this Proxy[ " + clazz + " ]into your presenter ?");
        }
        return t;
    }

    @Nullable
    final public A getView() {
        Preconditions.checkMainThread();
        A a = reference.get();
        if (a == null) {
            VLog.e("TBasePresenter", "getView is null");
        }
        return a;
    }

    @Nullable
    final public A getLifeCircle() {
        return reference == null ? null : reference.get();
    }

    /**
     * attach to view
     */
    @CallSuper
    public void onAttachedView(A a) {
        detachedState.append("onAttached[").append(System.currentTimeMillis()).append("]");
        Preconditions.checkMainThread();
        this.reference = new WeakReference<>(a);
        onAddProxy();
        List<BroadcastProcessor> processors = new ArrayList<>();
        List<String> categories = new ArrayList<>();
        manager.notifyObjects(proxy -> {
            proxy.onRegisterListeners();
            proxy.onRegisterBroadcast(processors, categories);
        });
        onRegisterListeners();
        onRegisterBroadcast(processors, categories);
        if (!processors.isEmpty()) {
            broadcastSupport = new BroadcastSupport();
            broadcastSupport._registerBroadcast(processors, categories);
        }
    }

    protected void onAddProxy() {

    }

    /**
     * detach from view
     */
    public void onDetachedView() {
        detachedState.append(",onDetached[").append(System.currentTimeMillis()).append("](");
        if (reference != null) {
            A a = reference.get();
            Activity activity;
            if (a instanceof Activity) {
                activity = (Activity) a;
            } else {
                activity = LifeCycleHelper.findActivity(a);
            }
            if (activity != null) {
                if (activity.isFinishing()) {
                    detachedState.append("isFinishing)");
                } else {
                    detachedState.append("isNotFinishing)");
                }
            } else {
                detachedState.append("isNullActivity)");
            }
        } else {
            detachedState.append("isNullReference)");
        }
        manager.notifyObjects(proxy -> {
            proxy.onUnregisterListeners();
        });
        onUnregisterListeners();
        unregisterBroadcast();
        Preconditions.checkMainThread();
        if (reference != null) {
            reference.clear();
            reference = null;
        }
    }

    final protected void runUI(Runnable runnable) {
        runUI(0, runnable);
    }

    final protected void runOnMainThreadImmediately(Runnable runnable) {
        if (runnable == null || reference == null) {
            return;
        }
        A a = reference.get();
        Activity activity;
        if (a instanceof Activity) {
            activity = (Activity) a;
        } else {
            activity = LifeCycleHelper.findActivity(a);
        }
        ThreadUtil.runUiImmediately(new SafeRunnable(activity) {
            @Override
            protected void runSafely() {
                runnable.run();
            }
        });
    }

    final protected void runUI(long delay, Runnable runnable) {
        if (runnable == null || reference == null) {
            return;
        }
        A a = reference.get();
        Activity activity;
        if (a instanceof Activity) {
            activity = (Activity) a;
        } else {
            activity = LifeCycleHelper.findActivity(a);
        }
        if (activity == null || activity.isDestroyed()) {
            return;
        }
        SafeRunnable safeRunnable = new SafeRunnable(activity) {
            @Override
            protected void runSafely() {
                A a = getLifeCircle();
                if (a != null) {
                    runnable.run();
                } else {
                    dumpLifeCircle("runUI");
                }
            }
        };
        if (delay > 0) {
            ThreadPool.runUiDelayed(delay, safeRunnable);
        } else {
            ThreadUtil.runUi(safeRunnable);
        }
    }

    final public void dumpLifeCircle(String caller) {
            Map<String, String> extra = new HashMap<>();
            extra.put("caller", caller);
            extra.put("detachedState", detachedState + ",dump[" + System.currentTimeMillis() + "]");
            VLog.e("TBasePresenter", extra.toString());
    }

    public void runIO(Runnable runnable) {
        ThreadPool.runIO(runnable);
    }

    protected void onRegisterListeners() {
    }

    protected void onUnregisterListeners() {
    }

    /**
     * Register broadcast.
     *
     * @param processors 定义对应Action的广播处理器。
     * @param categories 为所有处理器定义匹配条件。接收广播时，广播中携带的所有条件必须是处理器中的条件的子集才可被接收。
     *                   例如：处理器条件定义了a，可以接收无条件和a条件的广播，不可接收a和b两条件的广播；
     *                   处理器条件定义了a和b，可以接收无条件广播、a条件广播、a和b两条件广播，但是无法接收abc三条件的广播。
     */
    protected void onRegisterBroadcast(@NonNull List<BroadcastProcessor> processors, @NonNull List<String> categories) {

    }

    private void unregisterBroadcast() {
        if (broadcastSupport != null) {
            broadcastSupport._unregisterBroadcast();
            broadcastSupport = null;
        }
    }
}
